import { getConfig } from '@/config'
import type { RouteConfigs } from '@/layout/types'
import { usePermissionStoreHook } from '@/store/modules/permission'
import { sessionKey, type DataInfo } from '@/utils/auth'
import { buildHierarchyTree } from '@/utils/tree'
import { cloneDeep, intersection, isAllEmpty, isUrl, storageSession } from '@pureadmin/utils'
import { useTimeoutFn } from '@vueuse/core'
import { isProxy, toRaw } from 'vue'
import {
  createWebHashHistory,
  createWebHistory,
  type RouteComponent,
  type RouteRecordNormalized,
  type RouteRecordRaw,
  type RouterHistory,
} from 'vue-router'
import { router } from './index'

const IFrame = () => import('@/layout/frameView.vue')
// https://cn.vitejs.dev/guide/features.html#glob-import
const modulesRoutes = import.meta.glob('/src/views/**/*.{vue,tsx}')

// 动态路由
import { getAsyncRoutesByOSType } from '@/api/routes'
import { isEmpty } from 'lodash-es'

function handRank(routeInfo: any) {
  const { name, path, parentId, meta } = routeInfo
  return isAllEmpty(parentId)
    ? isAllEmpty(meta?.rank) || (meta?.rank === 0 && name !== 'Home' && path !== '/')
      ? true
      : false
    : false
}

/** 按照路由中meta下的rank等级升序来排序路由 */
function sortByRankAsc(arr: any[]) {
  arr.forEach((v, index) => {
    // 当rank不存在时，根据顺序自动创建，首页路由永远在第一位
    if (handRank(v)) v.meta.rank = index + 2
  })
  return arr.sort((a: { meta: { rank: number } }, b: { meta: { rank: number } }) => {
    return a?.meta.rank - b?.meta.rank
  })
}

/** 过滤meta中showLink为false的菜单 */
function filterTree(data: RouteComponent[]) {
  const newTree = cloneDeep(data).filter(
    (v: { meta: { showLink: boolean } }) => v.meta?.showLink !== false
  )
  newTree.forEach((v: { children }) => v.children && (v.children = filterTree(v.children)))
  return newTree
}

/** 过滤children长度为0的的目录，当目录下没有菜单时，会过滤此目录，目录没有赋予roles权限，当目录下只要有一个菜单有显示权限，那么此目录就会显示 */
function filterChildrenTree(data: RouteComponent[]) {
  const newTree = cloneDeep(data).filter((v: any) => v?.children?.length !== 0)
  newTree.forEach((v: { children }) => v.children && (v.children = filterTree(v.children)))
  return newTree
}

/** 判断两个数组彼此是否存在相同值 */
function isOneOfArray(a: Array<string>, b: Array<string>) {
  return Array.isArray(a) && Array.isArray(b)
    ? intersection(a, b).length > 0
      ? true
      : false
    : true
}

/** 从sessionStorage里取出当前登陆用户的角色roles，过滤无权限的菜单 */
function filterNoPermissionTree(data: RouteComponent[]) {
  const currentRoles = storageSession().getItem<DataInfo<number>>(sessionKey)?.roles ?? []
  const newTree = cloneDeep(data).filter((v: any) => isOneOfArray(v.meta?.roles, currentRoles))
  newTree.forEach((v: any) => v.children && (v.children = filterNoPermissionTree(v.children)))
  return filterChildrenTree(newTree)
}

/** 批量删除缓存路由(keepalive) */
function delAliveRoutes(delAliveRouteList: Array<RouteConfigs>) {
  delAliveRouteList.forEach((route) => {
    usePermissionStoreHook().cacheOperate({
      mode: 'delete',
      name: route?.name,
    })
  })
}

/** 通过path获取父级路径 */
function getParentPaths(path: string, routes: RouteRecordRaw[]) {
  // 深度遍历查找
  function dfs(routes: RouteRecordRaw[], path: string, parents: string[]) {
    for (let i = 0; i < routes.length; i++) {
      const item = routes[i]
      // 找到path则返回父级path
      if (item.path === path) return parents
      // children不存在或为空则不递归
      if (!item.children || !item.children.length) continue
      // 往下查找时将当前path入栈
      parents.push(item.path)

      if (dfs(item.children, path, parents).length) return parents
      // 深度遍历查找未找到时当前path 出栈
      parents.pop()
    }
    // 未找到时返回空数组
    return []
  }

  return dfs(routes, path, [])
}

/** 查找对应path的路由信息 */
function findRouteByPath(path: string, routes: RouteRecordRaw[]) {
  let res = routes.find((item: { path: string }) => item.path == path)
  if (res) {
    return isProxy(res) ? toRaw(res) : res
  } else {
    for (let i = 0; i < routes.length; i++) {
      if (routes[i].children instanceof Array && routes[i].children.length > 0) {
        res = findRouteByPath(path, routes[i].children)
        if (res) {
          return isProxy(res) ? toRaw(res) : res
        }
      }
    }
    return null
  }
}

function appendFallbackRoutes() {
  if (!router.hasRoute('pathMatch')) {
    router.addRoute({
      path: '/:pathMatch(.*)',
      name: 'pathMatch',
      redirect: '/error/404',
    })
  }
}

/** 处理动态路由（后端返回的路由） */
function handleAsyncRoutes(routeList) {
  if (!isEmpty(routeList)) {
    const rootRoute = router.getRoutes().find((v) => v.path === '/')
    flattenRoutes(formatAsyncRoutes(routeList)).forEach((v: RouteRecordRaw) => {
      // 防止重复添加路由
      if (rootRoute.children.findIndex((value) => value.path === v.path) === -1) {
        // 切记将路由push到routes后还需要使用addRoute，这样路由才能正常跳转
        rootRoute.children.push(v)
        sortByRankAsc(rootRoute.children)
        if (!router.hasRoute(v?.name)) router.addRoute(v)
      }
    })
    router.addRoute(rootRoute)
  }
  usePermissionStoreHook().handleWholeMenus(routeList)
  appendFallbackRoutes()
}

/** 初始化路由（`new Promise` 写法防止在异步请求中造成无限循环）*/
function initRouter() {
  if (getConfig()?.CachingAsyncRoutes) {
    // 开启动态路由缓存本地sessionStorage
    const key = 'async-routes'
    const asyncRouteList = storageSession().getItem(key) as any
    if (asyncRouteList && asyncRouteList?.length > 0) {
      return new Promise((resolve) => {
        handleAsyncRoutes(asyncRouteList)
        resolve(router)
      })
    } else {
      return new Promise((resolve) => {
        getAsyncRoutesByOSType([0, getConfig()?.OSType]).then(({ data }) => {
          handleAsyncRoutes(cloneDeep(data))
          storageSession().setItem(key, data)
          resolve(router)
        })
      })
    }
  } else {
    return new Promise((resolve) => {
      getAsyncRoutesByOSType([0, getConfig()?.OSType]).then(({ data }) => {
        handleAsyncRoutes(cloneDeep(data))
        resolve(router)
      })
    })
  }
}

/**
 * 将多级嵌套路由处理成一维数组
 * @param routesList 传入路由
 * @returns 返回处理后的一维路由
 */
function flattenRoutes(routesList: RouteRecordRaw[]) {
  if (routesList.length === 0) return routesList
  let hierarchyList = buildHierarchyTree(routesList)
  for (let i = 0; i < hierarchyList.length; i++) {
    if (hierarchyList[i].children) {
      hierarchyList = hierarchyList
        .slice(0, i + 1)
        .concat(hierarchyList[i].children, hierarchyList.slice(i + 1))
    }
  }
  return hierarchyList
}

/**
 * 一维数组处理成多级嵌套数组（三级及以上的路由全部拍成二级，keep-alive 只支持到二级缓存）
 * https://github.com/xiaoxian521/vue-pure-admin/issues/67
 * @param routesList 处理后的一维路由菜单数组
 * @returns 返回将一维数组重新处理成规定路由的格式
 */
function formatTwoStageRoutes(routesList: RouteRecordRaw[]) {
  if (routesList.length === 0) return routesList
  const newRoutesList: RouteRecordRaw[] = []
  routesList.forEach((v: RouteRecordRaw) => {
    if (v.path === '/') {
      newRoutesList.push({
        component: v.component,
        name: v.name,
        path: v.path,
        redirect: v.redirect,
        meta: v.meta,
        children: [],
      })
    } else {
      newRoutesList[0]?.children.push({ ...v })
    }
  })
  return newRoutesList
}

/** 处理缓存路由（添加、删除、刷新） */
function handleCacheRoute(matched: RouteRecordNormalized[], mode?: string) {
  switch (mode) {
    case 'add':
      matched.forEach((v) => {
        usePermissionStoreHook().cacheOperate({ mode: 'add', name: v.name })
      })
      break
    case 'delete':
      usePermissionStoreHook().cacheOperate({
        mode: 'delete',
        name: matched[matched.length - 1].name,
      })
      break
    default:
      usePermissionStoreHook().cacheOperate({
        mode: 'delete',
        name: matched[matched.length - 1].name,
      })
      useTimeoutFn(() => {
        matched.forEach((v) => {
          usePermissionStoreHook().cacheOperate({ mode: 'add', name: v.name })
        })
      }, 100)
  }
}

/** 过滤后端传来的动态路由 重新生成规范路由 */
function formatAsyncRoutes(arrRoutes: Array<RouteRecordRaw>) {
  if (!arrRoutes || !arrRoutes.length) return
  const modulesRoutesKeys = Object.keys(modulesRoutes)
  arrRoutes.forEach((v: RouteRecordRaw) => {
    // 将backstage属性加入meta，标识此路由为后端返回路由
    v.meta = v.meta ?? {}
    v.meta.backstage = true
    // 父级的redirect属性取值：如果子级存在且父级的redirect属性不存在，默认取第一个子级的path；如果子级存在且父级的redirect属性存在，取存在的redirect属性，会覆盖默认值
    if (v?.children && v.children.length && !v.redirect) v.redirect = v.children[0].path
    // 父级的name属性取值：如果子级存在且父级的name属性不存在，默认取第一个子级的name；如果子级存在且父级的name属性存在，取存在的name属性，会覆盖默认值（注意：测试中发现父级的name不能和子级name重复，如果重复会造成重定向无效（跳转404），所以这里给父级的name起名的时候后面会自动加上`Parent`，避免重复）
    if (v?.children && v.children.length && !v.name)
      v.name = (v.children[0].name as string) + 'Parent'
    if (v.meta?.frameSrc) {
      v.component = IFrame
    } else {
      // 处理外链路由
      if (isUrl(v.path)) {
        v.name = v.path
        v.path = '/externalLink'
      } else {
        // 兼容后端数据
        v.path = /^\/.*/.test(v.path) ? v.path : `/${v.path}`
      }
      // 对后端传component组件路径和不传做兼容（如果后端传component组件路径，那么path可以随便写，如果不传，component组件路径会跟path保持一致）
      let index = v?.component
        ? modulesRoutesKeys.findIndex((ev) => ev.includes(v.component as any))
        : modulesRoutesKeys.findIndex((ev) => ev.includes(v.path))
      if (index === -1) {
        index = modulesRoutesKeys.findIndex((ev) => ev.includes('404'))
      }
      v.component = modulesRoutes[modulesRoutesKeys[index]]
    }

    // 处理隐藏逻辑
    v.meta.showLink = !(v as any).hidden
    v.meta.hiddenTag = v.hidden
    // 默认展开父目录
    v.meta.showParent = true

    if (v?.children && v.children.length) {
      formatAsyncRoutes(v.children)
    }
  })
  return arrRoutes
}

/** 获取路由历史模式 https://next.router.vuejs.org/zh/guide/essentials/history-mode.html */
function getHistoryMode(): RouterHistory {
  const routerHistory = import.meta.env.VITE_ROUTER_HISTORY
  // len为1 代表只有历史模式 为2 代表历史模式中存在base参数 https://next.router.vuejs.org/zh/api/#%E5%8F%82%E6%95%B0-1
  const historyMode = routerHistory.split(',')
  const leftMode = historyMode[0]
  const rightMode = historyMode[1]
  // no param
  if (historyMode.length === 1) {
    if (leftMode === 'hash') {
      return createWebHashHistory('')
    } else if (leftMode === 'h5') {
      return createWebHistory('')
    }
  } //has param
  else if (historyMode.length === 2) {
    if (leftMode === 'hash') {
      return createWebHashHistory(rightMode)
    } else if (leftMode === 'h5') {
      return createWebHistory(rightMode)
    }
  }
}

export {
  delAliveRoutes,
  filterNoPermissionTree,
  filterTree,
  findRouteByPath,
  flattenRoutes,
  formatAsyncRoutes,
  formatTwoStageRoutes,
  getHistoryMode,
  getParentPaths,
  handleCacheRoute,
  initRouter,
  isOneOfArray,
  sortByRankAsc,
}
